﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.OleDb;
using System.IO;
using System.Web.UI;
using Telerik;
using Telerik.Blogs;
using Telerik.Cms.Engine;

namespace SEVDNUG.Web.Sitefinity.Tools.DataImporter
{
    /// <summary>
    /// Provides the ability to import Generic Content items into Sitefinity
    /// </summary>
    public class ImportData: ITool 
    {
        private ICollection<string> _providers;
        private ContentManager _contentManager;
        private IDictionary<string, IMetaInfo> _metaKeys;
        private string _filename;
        private List<string> _fieldNames;

        private const string CONTENT_TYPE = "text/html";
        private const string CONTENT_KEY = "Content";
        private const string BLOGID_KEY = "BlogID";
        private const string BLOGS = "Blogs";

        #region Implementation of ITool

        /// <summary>
        /// The name of the tool
        /// </summary>
        public string Name { get { return Resources.ImportData.tool_CommandPanel_Name; } }
        /// <summary>
        /// The title of the tool.
        /// </summary>
        public string Title { get { return Resources.ImportData.tool_ControlPanel_Title; } }
        /// <summary>
        /// The description of the tool.
        /// </summary>
        public string Description { get {return Resources.ImportData.tool_Description; } }

        /// <summary>
        /// Creates the Control Panel
        /// </summary>
        /// <param name="parent"></param>
        /// <returns></returns>
        public Control CreateControlPanel(TemplateControl parent)
        {
            return new ControlPanel(this, Resources.ImportData.tool_ControlPanel_Title);
        }
        #endregion

        /// <summary>
        /// The Meta Key name used for the Content
        /// </summary>
        public string ContentKey
        {
            get
            {
                return CONTENT_KEY;
            }
        }

        /// <summary>
        /// Gets the Meta Key field used to represent the BlogId.
        /// </summary>
        /// <value>The blog id key.</value>
        public string BlogIdKey
        {
            get
            {
                return BLOGID_KEY;
            }
        }

        #region Custom Methods

        /// <summary>
        /// Returns a list of providers
        /// </summary>
        public ICollection<string> GetProviders()
        {
            if (_providers == null)
            {
                _providers = ContentManager.Providers.Keys;
            }
            return _providers;
        }

        /// <summary>
        /// Gets the content manager.
        /// </summary>
        /// <param name="providerName">Name of the provider.</param>
        /// <returns></returns>
        public ContentManager GetContentManager(string providerName)
        {
            if (_contentManager == null)
            {
                _contentManager = new ContentManager(providerName);
            }
            else
            {
                if (_contentManager.Provider.Name != providerName)
                {
                    _contentManager = new ContentManager(providerName);
                }
            }
            return _contentManager;
        }

        /// <summary>
        /// Returns a list of the Meta Keys available for the given Provider
        /// </summary>
        /// <param name="providerName">The name of the provider</param>
        /// <returns></returns>
        public IDictionary<string, IMetaInfo> GetMetaKeys (string providerName)
        {

            if (_contentManager == null || providerName != _contentManager.Provider.Name)
            {
                ContentManager manager = GetContentManager(providerName);

                IContent content = manager.CreateContent(CONTENT_TYPE);
                _metaKeys = manager.GetMetaKeys(content);
                manager.CancelChanges(content.ID);
            }

            return _metaKeys;
        }


        /// <summary>
        /// Gets the field names for the specified files.
        /// </summary>
        /// <param name="filename">The filename.</param>
        /// <returns>A List of field names.</returns>
        public List<string> GetFieldNames(string filename)
        {
            DataTable dt;
            List<string> fields = new List<string>();

            if (filename == _filename && _fieldNames != null)
                return _fieldNames;


            dt = getFileContents(filename);

            if (dt == null) return fields;

            // TODO: Handler multiple files
            // Right now only handle the first Table/File
            fields.Clear();
            foreach (DataColumn dc in dt.Columns)
            {
                fields.Add(dc.ColumnName);
            }

            _filename = filename;
            _fieldNames = fields;
            return fields;

        }

        /// <summary>
        /// Reads the contents of the specified file to a <see cref="DataTable"/>.
        /// </summary>
        /// <param name="filename">The filename.</param>
        /// <returns></returns>
        private DataTable getFileContents(string filename)
        {

            if (File.Exists(filename) == false) return null;

            DataTable dt = ReadDelimitedFile(filename);

            return dt;

        }

        /// <summary>
        /// Reads the delimited file.
        /// </summary>
        /// <param name="filename">The filename.</param>
        /// <returns>
        /// A <see cref="DataSet"/> with the contents of the file
        /// </returns>
        public DataTable ReadDelimitedFile(string filename)
        {
            string connectionString;
            string commandText;

            FileInfo fi = new FileInfo(filename);
            OleDbConnection conn;
            OleDbDataAdapter da;
            DataTable dt = new DataTable();

            if (fi.Exists == false) return null;

            connectionString = "Provider=Microsoft.Jet.OLEDB.4.0;";
            connectionString += string.Format("Data Source={0};", fi.DirectoryName);
            connectionString += "Extended Properties='text;HDR=Yes'";

            commandText = string.Format("SELECT * FROM {0}", fi.Name);

            conn = new OleDbConnection(connectionString);
            conn.Open();

            da = new OleDbDataAdapter(commandText, conn);
            da.Fill(dt);
            dt.TableName = Path.GetFileNameWithoutExtension(fi.Name);

            return dt;

        }

        /// <summary>
        /// Imports the specified files.
        /// </summary>
        /// <param name="mappings">The mappings.</param>
        /// <param name="providerName">Name of the provider.</param>
        /// <param name="files">The files.</param>
        /// <returns>A <see cref="Results"/> object.</returns>
        public Results Import(BindingList<ImportDataMapping> mappings, string providerName, List<string> files)
        {
            Results results = new Results();
            
            foreach (string file in files)
            {
                Results result = Import(mappings, providerName, file);
                results.AddTo(result);
            }

            return results;
        }

        /// <summary>
        /// Imports the specified file.
        /// </summary>
        /// <param name="mappings">The mappings.</param>
        /// <param name="providerName">Name of the provider.</param>
        /// <param name="file">The file.</param>
        /// <returns>A <see cref="Results"/> object.</returns>
        public Results Import(BindingList<ImportDataMapping> mappings, string providerName, string file)
        {

            Results results = new Results();

            // Get the Manager Object
            ContentManager mgr = GetContentManager(providerName);
            BlogManager blogMgr;
            bool isBlog = (mgr.ModuleName == BLOGS);

            if (isBlog)
            {
                // Things get handled differently for blogs
                blogMgr = new BlogManager(providerName);
                mgr = blogMgr.Content;
            }

            DataTable dt = getFileContents(file);

            // Iterate through each row in the file
            foreach (DataRow row in dt.Rows)
            {
                
                try
                {
                    // Create the content
                    setMetaDataFields(mgr, mappings, row);
                    results.TotalImportedRecords++;
                }
                catch (Exception ex)
                {
                    results.ImportExceptions.Add(new ImportException(results.TotalRecords, ex.Message, ex));
                    results.TotalFailedRecords++;
                }
                results.TotalRecords++;

            }

            return results;
        }

        /// <summary>
        /// Sets the meta data fields.
        /// </summary>
        /// <param name="mgr">The Content Manager.</param>
        /// <param name="mappings">The mappings.</param>
        /// <param name="row">The row.</param>
        private void setMetaDataFields(ContentManager mgr, IEnumerable<ImportDataMapping> mappings, DataRow row)
        {

            IContent content = mgr.CreateContent(CONTENT_TYPE);

            foreach (ImportDataMapping mapping in mappings)
            {
                if (mapping.MetaKey == ContentKey)
                {
                    content.Content = row[mapping.FieldName];
                }
                else if (mapping.MetaKey == BlogIdKey)
                {
                    content.ParentID = new Guid(row[mapping.FieldName].ToString());
                }
                else
                {
                    object value = null;
                    switch (mgr.MetaKeys[mapping.MetaKey].ValueType)
                    {
                        case MetaValueTypes.Boolean:
                            value = string.IsNullOrEmpty(mapping.FixedValue) ? bool.Parse(row[mapping.FieldName].ToString()) : bool.Parse(mapping.FixedValue);
                            break;
                        case MetaValueTypes.DateTime:
                            value = string.IsNullOrEmpty(mapping.FixedValue) ? DateTime.Parse(row[mapping.FieldName].ToString()) : DateTime.Parse(mapping.FixedValue);
                            break;

                        case MetaValueTypes.FloatingPoint:
                            value = string.IsNullOrEmpty(mapping.FixedValue) ? bool.Parse(row[mapping.FieldName].ToString()) : bool.Parse(mapping.FixedValue);
                            break;
                        case MetaValueTypes.Guid:
                            value = string.IsNullOrEmpty(mapping.FixedValue) ? new Guid(row[mapping.FieldName].ToString()) : new Guid(mapping.FixedValue);
                            break;

                        case MetaValueTypes.Integer:
                            value = string.IsNullOrEmpty(mapping.FixedValue) ? int.Parse(row[mapping.FieldName].ToString()) : int.Parse(mapping.FixedValue);
                            break;

                        case MetaValueTypes.LongText:
                        case MetaValueTypes.ShortText:
                            value = string.IsNullOrEmpty(mapping.FixedValue) ? row[mapping.FieldName].ToString() : mapping.FixedValue;
                            break;
                    }

                    content.SetMetaData(mapping.MetaKey, value);
                }
            }

            mgr.SaveContent(content);
        }

        #endregion Custom Methods
    }
}